package com.file;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import javax.sound.sampled.*;
import java.io.*;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

public class Main {
    public static void main(String[] args) {
        // 打印可用音频设备
        printAudioDevices();

        // 启动音频处理线程
        Thread audioThread = new Thread(new AudioProcessor());
        audioThread.start();

        System.out.println("音频处理线程已启动...");
    }

    private static void printAudioDevices() {
        Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
        System.out.println("可用音频设备:");
        for (Mixer.Info info : mixerInfos) {
            System.out.println(" - " + info.getName() + " (" + info.getDescription() + ")");
        }
    }
}

class AudioProcessor implements Runnable {
    private static final float SAMPLE_RATE = 48000;  // 提升采样率
    private static final int SAMPLE_SIZE = 16;
    private static final int CHANNELS = 1;
    private static final double DB_THRESHOLD = 30.0; // 调整合理阈值
    private static final long SILENCE_TIMEOUT = 2000; // 2秒静默停止
    private static final long DB_DISPLAY_INTERVAL = 500; // 500ms显示一次

    private volatile boolean isRecording = false;
    private final AtomicLong lastLoudTime = new AtomicLong();
    private final AtomicBoolean sendFlag = new AtomicBoolean(false);
    private final AtomicLong lastDbDisplayTime = new AtomicLong();

    @Override
    public void run() {
        AudioFormat format = new AudioFormat(
                SAMPLE_RATE,
                SAMPLE_SIZE,
                CHANNELS,
                true,  // signed
                false  // little-endian（更兼容的设置）
        );

        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);

        try {
            TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(adjustFormatIfNeeded(line));
            line.start();

            System.out.println("实际音频格式: " + line.getFormat());
            System.out.println("缓冲区大小: " + line.getBufferSize() + " bytes");

            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];

            // 启动监控线程
            new Thread(() -> monitorVolume(line, out)).start();

            while (true) {
                if (sendFlag.getAndSet(false)) {
                    File tempFile = saveToFile(out.toByteArray(), line.getFormat());
                    sendToAPI(tempFile);
                    out.reset();
                }
                Thread.sleep(100);
            }
        } catch (Exception e) {
            System.err.println("音频处理异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private AudioFormat adjustFormatIfNeeded(TargetDataLine line) throws LineUnavailableException {
        try {
            line.open(line.getFormat());
            return line.getFormat();
        } catch (LineUnavailableException ex) {
            System.out.println("尝试兼容格式...");
            AudioFormat fallbackFormat = new AudioFormat(
                    SAMPLE_RATE,
                    SAMPLE_SIZE,
                    CHANNELS,
                    true,
                    true  // 回退到大端序
            );
            line.open(fallbackFormat);
            return fallbackFormat;
        }
    }

    private void monitorVolume(TargetDataLine line, ByteArrayOutputStream out) {
        byte[] buffer = new byte[4096];
        while (true) {
            int bytesRead = line.read(buffer, 0, buffer.length);
            if (bytesRead > 0) {
                double currentDb = calculateDbLevel(buffer, bytesRead);

                // 控制台显示（限流）
                long now = System.currentTimeMillis();
                if (now - lastDbDisplayTime.get() >= DB_DISPLAY_INTERVAL) {
                    System.out.printf("[%tT] 当前音量: %.2f dB%n", now, currentDb);
                    lastDbDisplayTime.set(now);
                }

                if (currentDb > DB_THRESHOLD) {
                    if (!isRecording) {
                        isRecording = true;
                        System.out.printf("[%tT] 开始录音...%n", now);
                    }
                    lastLoudTime.set(now);
                    out.write(buffer, 0, bytesRead);
                }

                if (isRecording && (now - lastLoudTime.get() > SILENCE_TIMEOUT)) {
                    isRecording = false;
                    System.out.printf("[%tT] 停止录音（静默超时）%n", now);
                    sendFlag.set(true);
                }
            }
        }
    }

    private double calculateDbLevel(byte[] audioData, int length) {
        if (length < 2) return -100;

        short[] amplitudes = new short[length / 2];
        ByteBuffer.wrap(audioData, 0, length)
                .order(ByteOrder.LITTLE_ENDIAN)
                .asShortBuffer()
                .get(amplitudes);

        double sum = 0;
        int validSamples = 0;
        for (short amp : amplitudes) {
            if (amp != 0) {
                sum += amp * amp;
                validSamples++;
            }
        }

        if (validSamples == 0) return -100;

        double rms = Math.sqrt(sum / validSamples);
        return 20 * Math.log10(rms);
    }

    private File saveToFile(byte[] audioData, AudioFormat format) throws IOException {
        Path savePath = Paths.get("recordings");
        if (!Files.exists(savePath)) {
            Files.createDirectories(savePath);
        }

        String filename = String.format("%tY%<tm%<td_%<tH%<tM%<tS.wav", System.currentTimeMillis());
        File audioFile = savePath.resolve(filename).toFile();

        try (AudioInputStream stream = new AudioInputStream(
                new ByteArrayInputStream(audioData),
                format,
                audioData.length / format.getFrameSize()
        )) {
            AudioSystem.write(stream, AudioFileFormat.Type.WAVE, audioFile);
        }
        System.out.println("文件已保存: " + audioFile.getAbsolutePath());
        return audioFile;
    }

    private void sendToAPI(File audioFile) {
        HttpClient client = HttpClient.newHttpClient();
        JsonObject jsonBodyObj = new JsonObject();
        jsonBodyObj.addProperty("file_path", audioFile.getAbsolutePath());

        Gson gson = new Gson();
        String jsonBody = gson.toJson(jsonBodyObj);
        System.out.println(jsonBody);

        // 创建HTTP请求
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://localhost:8000/recognize/file"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
                .build();

        // 发送请求并处理响应
        try {
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            if (response.statusCode() == 200) {
                System.out.println("响应数据：" + response.body());
            } else {
                System.err.println("API请求失败，状态码：" + response.statusCode());
            }
        } catch (Exception e) {
            System.err.println("API请求失败：" + e.getMessage());
        }
    }
}